#include <iostream>
#include <memory>
#include <boost/asio.hpp>

using namespace boost::asio;
using namespace boost::asio::ip;
using namespace std;

//会话类，处理单个客户端连接
// public enable_shared_from_this<Session> 是 C++ 标准库中的一个模板类，
// 用于安全地从对象内部获取指向自身的 shared_ptr
class Session : public enable_shared_from_this<Session> {

public:
    Session(tcp::socket socket) : socket_(std::move(socket)) {}

    void start() {
        read();
    }
    
private:
    // 读取数据
    void read() {
        //这样子是为了增加这个实例本身的引用计数
        //因为初次调用时只是注册，如果不这样，
        // 下面的回调真正执行时可能session已经离开作用域，引用技术归0
        // 引用计数变化：
        // 初始：Session 对象引用计数 = 1 (由外部 shared_ptr 持有)
        // ↓
        // 调用 shared_from_this() 后：引用计数 = 2 (外部 + self)
        // ↓
        // 外部 shared_ptr 销毁：引用计数 = 1 (仅 self 持有)
        // ↓
        // Lambda 执行完毕，self 销毁：引用计数 = 0，对象被删除
        auto self(shared_from_this()); // 获取指向自身的 shared_ptr
        // 这里只是注册回调，还没有实际调用
        //当 async_read_some 注册回调时，Boost.Asio 会保存整个 lambda 对象，包括其中捕获的变量
        //   shared_ptr<Session> session -> Session 对象
        //                       ↑
        //                       |
        //             lambda 中捕获的 self
        //                       ↑
        //                       |
        //            Boost.Asio 内部保存的回调
        //auto self(shared_from_this()) 能确保对象存活的原因是：
        // Lambda 捕获：self 被复制到 lambda 中，延长了其生命周期
        // Boost.Asio 保存回调：整个 lambda 对象被 Boost.Asio 内部保存
        // 引用计数机制：直到回调执行完毕并被销毁，对象才会被释放
        // 作用域独立：lambda 的生命周期独立于创建它的函数作用域
        socket_.async_read_some(buffer(buffer_),
            [this, self](boost::system::error_code ec, size_t length) {
            // 这个lambda不会立刻执行，而是在有数据可读时才会被触发执行
            // 当 lambda 捕获 self 时，Session 对象的引用计数至少为 1
            // 确保在异步操作完成前对象不会被销毁
            if (!ec) {
                std::cout << "Received: " << std::string(self->buffer_, length) << "\n";
                //回显数据
                write(length);
            }
            else {
                std::cerr << "Read error: " << ec.message() << std::endl;
            }
        });
    }

    // 写入数据
    void write(size_t length) {
        auto self(shared_from_this());
        async_write(socket_, buffer(buffer_, length),
            [this, self](boost::system::error_code ec, size_t /*length*/) {
            if (!ec) {
                // 继续读取数据
                read();
            }
            else {
                std::cerr << "Write error: " << ec.message() << std::endl;
            }
        });
    }

    tcp::socket socket_;
    char buffer_[1024];
};

//服务器类
class Server {

public:
    Server(io_context& io_context, unsigned short port) : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), socket_(io_context) {
        start_accept();
    }

private:
    // 接受新连接
    // 1. Server 构造函数调用 start_accept()
    //    ↓
    // 2. 注册第一个 async_accept 操作，立即返回
    //    ↓
    // 3. io_context.run() 启动事件循环
    //    ↓
    // 4. 第一个客户端连接到达
    //    ↓
    // 5. 回调函数执行，处理连接
    //    ↓
    // 6. 回调函数中调用 start_accept() 注册下一个 async_accept
    //    ↓
    // 7. 回到事件循环，等待下一个连接
    //    ↓
    // 8. 第二个客户端连接到达
    //    ↓
    // 9. 回调函数执行，处理连接
    //    ↓
    // 10. 再次调用 start_accept() 注册下一个 async_accept
    //    ↓
    // ... 依此类推
    void start_accept() {
        //注册回调，lambda函数不会立刻执行，有socket连接时才会触发
        //注册完立刻返回不会阻塞
        acceptor_.async_accept(socket_,
            [this](boost::system::error_code ec) {
            if (!ec) {
                // 创建会话对象并启动
                // 这个socket已经被用了，所以要std::move 到这里新的session里，
                // 才能让类的socket继续接受下一个
                auto session = make_shared<Session>(std::move(socket_));
                session->start();
            }
            else {
                std::cerr << "Accept error: " << ec.message() << std::endl;
            }

            // 继续接受新连接，因为async_accept调用一次就返回了，所以要再次调用
            start_accept();
        });
    }

    tcp::acceptor acceptor_;
    tcp::socket socket_;
};

int main() {
    try {
        io_context io_context;
        Server server(io_context, 8888);
        std::cout << "Async Server started, listening on port 8888..." << std::endl;

        // 运行io_context
        io_context.run();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}